#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
主窗口模块
包含应用程序的主窗口类
"""

import os
import sys
import logging
import configparser
from pathlib import Path

from PyQt5.QtWidgets import (
    QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QTextEdit, QComboBox, QLabel, QSlider, QFileDialog,
    QStatusBar, QGroupBox, QGridLayout, QSpinBox, QDoubleSpinBox,
    QSplitter, QTabWidget, QCheckBox, QMessageBox, QProgressBar, QMenu, QAction
)
from PyQt5.QtCore import Qt, QSettings
from PyQt5.QtGui import QFont, QIcon, QTextCursor

# 导入项目模块
from core.tts_engine import TTSEngine
from core.model_manager import ModelManager
from config.paths import CONFIG_FILE
from gui.thread_workers import ModelDownloadThread, TTSThread
from gui.model_download_dialog import ModelDownloadDialog
from gui.nltk_download_dialog import NLTKDownloadDialog

# 配置日志
logger = logging.getLogger("主窗口")

class MainWindow(QMainWindow):
    """主窗口类"""
    
    def __init__(self):
        """初始化主窗口"""
        super().__init__()
        
        # 加载配置
        self.config = configparser.ConfigParser()
        if os.path.exists(CONFIG_FILE):
            self.config.read(CONFIG_FILE)
        else:
            self._create_default_config()
        
        # 创建TTS引擎
        self.tts_engine = TTSEngine(ui_callback=self.log)
        
        # 已合成的音频文件
        self.current_audio_file = None
        
        # 初始化界面
        self.init_ui()
        
        # 检查模型
        if self.config.getboolean('Download', 'check_models_on_startup', fallback=True):
            self.check_models()
            
        # 检查NLTK数据
        self._check_nltk_data()
    
    def _create_default_config(self):
        """创建默认配置"""
        self.config['General'] = {
            'default_speaker': 'Twilight Sparkle',
            'default_speed': '1.0',
            'language': 'zh',
            'theme': 'system'
        }
        self.config['Audio'] = {
            'sample_rate': '22050',
            'channels': '1',
            'bit_depth': '16',
            'output_format': 'wav'
        }
        self.config['Download'] = {
            'check_models_on_startup': 'true',
            'download_missing_models': 'true',
            'show_progress': 'true'
        }
        # 创建配置目录
        os.makedirs(os.path.dirname(CONFIG_FILE), exist_ok=True)
        # 保存默认配置
        with open(CONFIG_FILE, 'w') as f:
            self.config.write(f)
    
    def init_ui(self):
        """初始化用户界面"""
        # 窗口设置
        self.setWindowTitle("深度语音合成")
        self.setMinimumSize(800, 600)
        
        # 创建菜单栏
        menubar = self.menuBar()
        
        # 文件菜单
        file_menu = menubar.addMenu("文件")
        
        save_action = QAction("保存音频...", self)
        save_action.triggered.connect(self.on_save)
        file_menu.addAction(save_action)
        
        file_menu.addSeparator()
        
        exit_action = QAction("退出", self)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)
        
        # 工具菜单
        tools_menu = menubar.addMenu("工具")
        
        download_action = QAction("模型下载管理...", self)
        download_action.triggered.connect(self.show_download_dialog)
        tools_menu.addAction(download_action)
        
        nltk_action = QAction("NLTK数据下载...", self)
        nltk_action.triggered.connect(self.show_nltk_download_dialog)
        tools_menu.addAction(nltk_action)
        
        # 创建中心部件
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        
        # 主布局
        main_layout = QVBoxLayout(central_widget)
        
        # 创建标题标签
        title_label = QLabel("深度语音合成系统")
        title_label.setFont(QFont("Arial", 16, QFont.Bold))
        title_label.setAlignment(Qt.AlignCenter)
        main_layout.addWidget(title_label)
        
        # 创建分割器
        splitter = QSplitter(Qt.Vertical)
        main_layout.addWidget(splitter)
        
        # 创建上下两个面板
        top_panel = QWidget()
        bottom_panel = QWidget()
        splitter.addWidget(top_panel)
        splitter.addWidget(bottom_panel)
        
        # 设置面板比例
        splitter.setSizes([400, 200])
        
        # 上面板布局
        top_layout = QVBoxLayout(top_panel)
        
        # 文本输入区域
        text_group = QGroupBox("输入文本")
        text_layout = QVBoxLayout(text_group)
        
        self.text_edit = QTextEdit()
        self.text_edit.setPlaceholderText("请在此处输入要合成的文本...")
        text_layout.addWidget(self.text_edit)
        
        top_layout.addWidget(text_group)
        
        # 控制面板
        controls_group = QGroupBox("合成控制")
        controls_layout = QGridLayout(controls_group)
        
        # 角色选择
        controls_layout.addWidget(QLabel("角色:"), 0, 0)
        self.speaker_combo = QComboBox()
        self.populate_speakers()
        controls_layout.addWidget(self.speaker_combo, 0, 1)
        
        # 语速控制
        controls_layout.addWidget(QLabel("语速:"), 1, 0)
        speed_layout = QHBoxLayout()
        self.speed_slider = QSlider(Qt.Horizontal)
        self.speed_slider.setRange(50, 200)
        self.speed_slider.setValue(100)
        self.speed_slider.setTickPosition(QSlider.TicksBelow)
        self.speed_slider.setTickInterval(25)
        
        self.speed_spin = QDoubleSpinBox()
        self.speed_spin.setRange(0.5, 2.0)
        self.speed_spin.setSingleStep(0.1)
        self.speed_spin.setValue(1.0)
        
        # 连接速度控件信号
        self.speed_slider.valueChanged.connect(self.on_speed_slider_changed)
        self.speed_spin.valueChanged.connect(self.on_speed_spin_changed)
        
        speed_layout.addWidget(self.speed_slider)
        speed_layout.addWidget(self.speed_spin)
        controls_layout.addLayout(speed_layout, 1, 1)
        
        # 自动播放选项
        self.autoplay_check = QCheckBox("合成后自动播放")
        self.autoplay_check.setChecked(True)
        controls_layout.addWidget(self.autoplay_check, 2, 0)
        
        top_layout.addWidget(controls_group)
        
        # 动作按钮
        buttons_layout = QHBoxLayout()
        
        self.synthesize_btn = QPushButton("合成语音")
        self.synthesize_btn.clicked.connect(self.on_synthesize)
        buttons_layout.addWidget(self.synthesize_btn)
        
        self.play_btn = QPushButton("播放")
        self.play_btn.setEnabled(False)
        self.play_btn.clicked.connect(self.on_play)
        buttons_layout.addWidget(self.play_btn)
        
        self.save_btn = QPushButton("另存为...")
        self.save_btn.setEnabled(False)
        self.save_btn.clicked.connect(self.on_save)
        buttons_layout.addWidget(self.save_btn)
        
        top_layout.addLayout(buttons_layout)
        
        # 下面板布局 - 日志区域
        bottom_layout = QVBoxLayout(bottom_panel)
        
        log_group = QGroupBox("日志")
        log_layout = QVBoxLayout(log_group)
        
        self.log_text = QTextEdit()
        self.log_text.setReadOnly(True)
        log_layout.addWidget(self.log_text)
        
        bottom_layout.addWidget(log_group)
        
        # 状态栏
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)
        
        # 进度条
        self.progress_bar = QProgressBar()
        self.progress_bar.setFixedWidth(200)
        self.progress_bar.setVisible(False)
        self.status_bar.addPermanentWidget(self.progress_bar)
        
        # 设置默认状态
        self.status_bar.showMessage("就绪")
    
    def populate_speakers(self):
        """填充角色下拉列表"""
        self.speaker_combo.clear()
        
        try:
            # 获取角色信息 - 使用DeepPoniesTTS的speaker2id
            speaker2id = {}
            if hasattr(self.tts_engine, 'tts') and hasattr(self.tts_engine.tts, 'speaker2id'):
                speaker2id = self.tts_engine.tts.speaker2id
            
            if not speaker2id:
                # 添加一个默认角色
                self.speaker_combo.addItem("Twilight Sparkle", "Twilight Sparkle")
                return
            
            # 添加所有可用角色
            for speaker_name in speaker2id.keys():
                self.speaker_combo.addItem(speaker_name, speaker_name)
            
            # 尝试设置为默认角色
            default_speaker = self.config.get('General', 'default_speaker', fallback="Twilight Sparkle")
            index = self.speaker_combo.findData(default_speaker)
            if index >= 0:
                self.speaker_combo.setCurrentIndex(index)
        except Exception as e:
            self.log(f"加载角色列表出错: {str(e)}", is_error=True)
            # 添加一个默认角色作为后备
            self.speaker_combo.addItem("Twilight Sparkle", "Twilight Sparkle")
    
    def log(self, message, is_error=False):
        """
        添加日志消息
        
        Args:
            message: 日志消息
            is_error: 是否为错误消息
        """
        prefix = "[错误] " if is_error else ""
        self.log_text.append(f"{prefix}{message}")
        self.log_text.moveCursor(QTextCursor.End)
        if is_error:
            logger.error(message)
        else:
            logger.info(message)
    
    def check_models(self):
        """检查并下载模型"""
        self.log("正在检查模型...")
        self.progress_bar.setVisible(True)
        self.status_bar.showMessage("正在检查模型...")
        
        # 检查模型文件是否存在
        if hasattr(self.tts_engine, 'model_manager'):
            if self.tts_engine.model_manager.check_model_files():
                self.log("模型文件已准备就绪")
                self.progress_bar.setVisible(False)
                return
        
        # 创建并启动下载线程
        self.download_thread = ModelDownloadThread(ui_callback=self.log)
        self.download_thread.progress.connect(self.log)
        self.download_thread.finished.connect(self.on_model_check_finished)
        self.download_thread.start()
    
    def on_model_check_finished(self, success):
        """
        模型检查完成的回调
        
        Args:
            success: 是否成功
        """
        self.progress_bar.setVisible(False)
        
        if success:
            self.status_bar.showMessage("模型准备就绪")
            self.log("模型已准备就绪")
        else:
            self.status_bar.showMessage("模型检查完成，但有缺失")
            
            # 询问用户是否打开下载对话框
            response = QMessageBox.question(
                self, 
                "缺少模型", 
                "系统检测到缺少必要的模型文件。是否打开模型下载管理器？",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.Yes
            )
            
            if response == QMessageBox.Yes:
                self.show_download_dialog()
    
    def show_download_dialog(self):
        """显示模型下载对话框"""
        download_dialog = ModelDownloadDialog(self)
        download_dialog.exec_()
        
        # 对话框关闭后，检查模型文件
        if hasattr(self.tts_engine, 'model_manager'):
            if self.tts_engine.model_manager.check_model_files():
                self.log("所有模型文件已准备就绪")
            else:
                self.log("仍有模型文件缺失，某些功能可能不可用", True)
    
    def show_nltk_download_dialog(self):
        """显示NLTK数据下载对话框"""
        nltk_dialog = NLTKDownloadDialog(self)
        nltk_dialog.exec_()
        
        # 对话框关闭后，尝试重新初始化文本处理器
        try:
            self.log("重新初始化文本处理器...")
            self.tts_engine.text_processor = None  # 释放旧的处理器
            from utils.text_processing import TextProcessor  # 重新导入
            self.tts_engine.text_processor = TextProcessor()  # 创建新的处理器
            self.log("文本处理器已重新初始化")
        except Exception as e:
            self.log(f"文本处理器初始化失败: {str(e)}", True)
    
    def on_speed_slider_changed(self, value):
        """
        语速滑块值变化处理
        
        Args:
            value: 滑块位置(50-200)
        """
        speed = value / 100.0
        self.speed_spin.blockSignals(True)
        self.speed_spin.setValue(speed)
        self.speed_spin.blockSignals(False)
    
    def on_speed_spin_changed(self, value):
        """
        语速微调框值变化处理
        
        Args:
            value: 速度值(0.5-2.0)
        """
        slider_value = int(value * 100)
        self.speed_slider.blockSignals(True)
        self.speed_slider.setValue(slider_value)
        self.speed_slider.blockSignals(False)
    
    def on_synthesize(self):
        """合成按钮点击处理"""
        # 获取输入文本
        text = self.text_edit.toPlainText().strip()
        if not text:
            QMessageBox.warning(self, "输入错误", "请输入要合成的文本")
            return
        
        # 获取当前选定的角色
        speaker_name = self.speaker_combo.currentData()
        if not speaker_name:
            QMessageBox.warning(self, "选择错误", "请选择一个角色")
            return
        
        # 获取语速
        speed = self.speed_spin.value()
        
        # 禁用合成按钮
        self.synthesize_btn.setEnabled(False)
        self.progress_bar.setVisible(True)
        self.status_bar.showMessage("正在合成...")
        
        # 记录日志
        self.log(f"开始合成文本: '{text}'")
        self.log(f"使用角色: {speaker_name}, 语速: {speed}")
        
        # 创建并启动合成线程
        self.tts_thread = TTSThread(self.tts_engine, text, speaker_name, speed)
        self.tts_thread.progress.connect(self.log)
        self.tts_thread.finished.connect(self.on_synthesize_finished)
        self.tts_thread.start()
    
    def on_synthesize_finished(self, result):
        """
        合成完成的回调
        
        Args:
            result: (成功状态, 文件路径)元组
        """
        success, file_path = result
        
        # 恢复UI状态
        self.synthesize_btn.setEnabled(True)
        self.progress_bar.setVisible(False)
        
        if success and file_path:
            self.status_bar.showMessage(f"合成完成: {file_path}")
            
            # 保存当前音频文件
            self.current_audio_file = file_path
            
            # 启用播放和保存按钮
            self.play_btn.setEnabled(True)
            self.save_btn.setEnabled(True)
            
            # 自动播放
            if self.autoplay_check.isChecked():
                self.on_play()
        else:
            self.status_bar.showMessage("合成失败")
            self.log("合成失败，请检查模型和输入")
    
    def on_play(self):
        """播放按钮点击处理"""
        if not self.current_audio_file or not os.path.exists(self.current_audio_file):
            QMessageBox.warning(self, "播放错误", "没有可播放的音频文件")
            return
        
        self.log(f"播放音频: {self.current_audio_file}")
        
        # 使用系统命令播放
        if sys.platform == "win32":
            os.system(f'start {self.current_audio_file}')
        elif sys.platform == "darwin":  # macOS
            os.system(f'afplay {self.current_audio_file}')
        else:  # Linux
            os.system(f'aplay {self.current_audio_file}')
    
    def on_save(self):
        """另存为按钮点击处理"""
        if not self.current_audio_file or not os.path.exists(self.current_audio_file):
            QMessageBox.warning(self, "保存错误", "没有可保存的音频文件")
            return
        
        # 打开文件对话框
        file_dialog = QFileDialog()
        file_dialog.setAcceptMode(QFileDialog.AcceptSave)
        file_dialog.setNameFilter("WAV文件 (*.wav);;MP3文件 (*.mp3);;OGG文件 (*.ogg)")
        file_dialog.setDefaultSuffix("wav")
        
        if file_dialog.exec_():
            target_file = file_dialog.selectedFiles()[0]
            
            try:
                # 判断是否需要格式转换
                src_ext = os.path.splitext(self.current_audio_file)[1].lower()
                dst_ext = os.path.splitext(target_file)[1].lower()
                
                if src_ext == dst_ext:
                    # 直接复制
                    import shutil
                    shutil.copy2(self.current_audio_file, target_file)
                    self.log(f"文件已保存: {target_file}")
                else:
                    # 格式转换
                    from utils.audio_utils import AudioProcessor
                    format = dst_ext[1:]  # 去掉点号
                    AudioProcessor.convert_format(self.current_audio_file, format)
                    self.log(f"文件已转换并保存: {target_file}")
                
                self.status_bar.showMessage(f"文件已保存: {target_file}")
                
            except Exception as e:
                self.log(f"保存文件时出错: {str(e)}")
                QMessageBox.critical(self, "保存错误", f"保存文件时出错: {str(e)}")
    
    def closeEvent(self, event):
        """
        窗口关闭事件
        
        Args:
            event: 关闭事件
        """
        self.log("正在关闭程序...")
        
        # 保存配置
        try:
            with open(CONFIG_FILE, 'w') as f:
                self.config.write(f)
        except Exception as e:
            logger.error(f"保存配置文件时出错: {str(e)}")
        
        # 卸载模型
        if hasattr(self, 'tts_engine'):
            self.tts_engine.unload_models()
        
        # 接受关闭事件
        event.accept()

    def _check_nltk_data(self):
        """检查并下载NLTK数据"""
        self.log("正在检查NLTK数据...")
        self.progress_bar.setVisible(True)
        self.status_bar.showMessage("正在检查NLTK数据...")
        
        # 创建并启动下载线程
        self.nltk_download_thread = ModelDownloadThread(ui_callback=self.log, download_type='nltk')
        self.nltk_download_thread.progress.connect(self.log)
        self.nltk_download_thread.finished.connect(self.on_nltk_check_finished)
        self.nltk_download_thread.start()
    
    def on_nltk_check_finished(self, success):
        """
        NLTK数据检查完成的回调
        
        Args:
            success: 是否成功
        """
        self.progress_bar.setVisible(False)
        
        if success:
            self.status_bar.showMessage("NLTK数据已准备就绪")
        else:
            self.status_bar.showMessage("NLTK数据检查完成，但有缺失")
            
            # 询问用户是否打开下载对话框
            response = QMessageBox.question(
                self, 
                "缺少NLTK数据", 
                "系统检测到缺少必要的NLTK数据文件。是否打开NLTK数据下载管理器？",
                QMessageBox.Yes | QMessageBox.No,
                QMessageBox.Yes
            )
            
            if response == QMessageBox.Yes:
                self.show_nltk_download_dialog() 